package org.appframe4gwt.server.persistance;

import java.util.ArrayList;
import java.util.HashMap;
import javax.sql.DataSource;
import xq.gwt.mvc.model.BooleanPropertyModel;
import xq.gwt.mvc.model.EntityModel;
import xq.gwt.mvc.model.ListItem;
import xq.gwt.mvc.model.PersistableEntityModel;
import xq.gwt.mvc.model.PropertyModel;
import xq.gwt.mvc.model.PropertyRelation;
import xq.gwt.mvc.model.StringPropertyModel;
import xq.gwt.ui.crud.FetchedEntities;
import xq.gwt.ui.crud.FilterModel;
import xq.gwt.ui.crud.OrderByModel;
import xq.gwt.ui.crud.PersistanceService;
import xq.gwt.ui.crud.FilterModel.Operator;
import xq.gwt.ui.exception.SerializableException;
import xq.persistence.PersistenceManager;
import xq.persistence.TotalRowCount;
import xq.sql.clauses.OrderByClause;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class PersistanceServiceServlet extends RemoteServiceServlet implements PersistanceService {	
	
	private static final long serialVersionUID = 2150040029313540180L;
	protected PersistenceManager pm = new PersistenceManager();
		
	public void setDataSource(DataSource dataSource) {		
		this.pm.setDataSource(dataSource);	
	}

	@Override
	public PersistableEntityModel persistEntity(PersistableEntityModel entity) throws Exception {
		try {			
			if (entity == null)
				return null;
			
			if (entity.isNew()) {
				pm.insertEntity(entity);
			} else {
				pm.updateEntity(entity);
			}
		
			return entity;
		} catch(Exception e){
			e.printStackTrace();
			String message = e.getMessage() != null ? e.getMessage() : e.toString();
			throw new SerializableException(message, e);		
		}		
	}

	@Override
	public boolean deleteEntity(PersistableEntityModel entity) throws Exception {
		try {			
			pm.deleteEntity(entity);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			String message = e.getMessage() != null ? e.getMessage() : e.toString();
			throw new SerializableException(message, e);
		}
	}
	
	@Override
	public void deleteAll(PersistableEntityModel entity) throws Exception {
		try {
			pm.deleteAllWithoutNotification(entity);			
		} catch (Exception e) {
			e.printStackTrace();
			String message = e.getMessage() != null ? e.getMessage() : e.toString();
			throw new SerializableException(message, e);
		}
		
	}

	@Override
	public PersistableEntityModel[] fetchAll(PersistableEntityModel entity)	throws Exception {
		try {
			PersistableEntityModel[] result = null;
			ArrayList<PersistableEntityModel> resultList = pm.fetchAllEntities(entity);
			result = new PersistableEntityModel[resultList.size()];
			resultList.toArray(result);			

			return result;
		} catch (Exception e) {
			e.printStackTrace();
			String message = e.getMessage() != null ? e.getMessage() : e.toString();
			throw new SerializableException(message, e);			
		}
	}

	@Deprecated
	@Override
	public FetchedEntities fetch(PersistableEntityModel entity,	FilterModel filter) throws Exception {
		return fetch(entity,filter,null,null,null);		
	}

	@Deprecated
	@Override
	public FetchedEntities fetch(PersistableEntityModel entity,	FilterModel filter, OrderByModel[] orderBy) throws Exception {
		return fetch(entity, filter, orderBy, null, null);		
	}

	@Deprecated
	@Override
	public FetchedEntities fetch(PersistableEntityModel entity,	FilterModel filter, Integer offset, Integer maxRowCount) throws Exception {
		return fetch(entity,filter,null,offset,maxRowCount);		
	}

	@Override
	public FetchedEntities fetch(PersistableEntityModel entity,	FilterModel filter, OrderByModel[] orderBy, Integer offset, Integer maxRowCount) throws Exception {
		try {
			PersistableEntityModel[] entities = null;
			TotalRowCount totalRowCount =  new TotalRowCount();
			Integer maxRows = maxRowCount;
			if(maxRows == null){
				maxRows = 500;
			}
			
			OrderByClause orderByClause = null;
			if(orderBy != null){
				orderByClause = OrderByClauseBuilder.buildOrderByClause(orderBy);
			}			
			ArrayList<PersistableEntityModel> resultList = pm.fetch(entity,WhereClauseBuilder.build(filter), orderByClause,offset,maxRows,totalRowCount);
			entities = new PersistableEntityModel[resultList.size()];
			resultList.toArray(entities);
			FetchedEntities result = new FetchedEntities();
			result.entities = entities;
			result.outOfTotalEntities = totalRowCount.getCount();
			return result;
		} catch (Exception e) {	
			e.printStackTrace();
			String message = e.getMessage() != null ? e.getMessage() : e.toString();
			throw new SerializableException(message, e);
		}
		
	}

	@Override
	public PersistableEntityModel fetchEntity(PersistableEntityModel entity) throws Exception {
		try {
			pm.fetchEntity(entity);			
			return entity;
		} catch (Exception e) {
			e.printStackTrace();
			String message = e.getMessage() != null ? e.getMessage() : e.toString();
			throw new SerializableException(message, e);
		}
	}

	@Override
	public HashMap<String, ListItem[]> fetchEntityListItems(PersistableEntityModel prototype)
			throws Exception {
		try {

			EntityModel entity = prototype.newInstance();
			HashMap<String, ListItem[]> listItemsMap = new HashMap<String, ListItem[]>();
			for (PropertyModel prop  : entity.getProperties()) {				
				if(prop.getRelation() != null){
					ListItem[] listItems = listItemsFor(prop.getRelation());
					if(listItems != null){
						listItemsMap.put(prop.getName(), listItems);
					}
				}				
			}

			return listItemsMap;
		} catch (Exception e) {
			throw new SerializableException(e.getMessage(), e.getCause());
		}
		
	}
	
	protected FilterModel getListItemFilter(){
		FilterModel filterBlocked = new FilterModel();
		BooleanPropertyModel blocked = new BooleanPropertyModel();
		blocked.setName("BLOCKED");
		blocked.setValue(true);
		filterBlocked.setFieldValue(blocked);
		filterBlocked.setOperator(Operator.isNot);
		return filterBlocked;
	}
	
	protected ListItem[] listItemsFor(PropertyRelation relation) throws Exception{
		PersistableEntityModel relatedEntity = (PersistableEntityModel)relation.getToEntity().newInstance();
		
		
		String relatedPropName = relation.getToPropertyName();
		OrderByModel orderby = new OrderByModel();
		orderby.propertyName = relatedPropName;						
		PersistableEntityModel[] entities = fetch(relatedEntity, getListItemFilter(), new OrderByModel[]{orderby}).entities;
		if (entities != null) {					
			ListItem[] listItems = new ListItem[entities.length];

			for (int i = 0; i < entities.length; i++) {
				PropertyModel relatedProp = entities[i].getProperty(relatedPropName);
				if (relatedProp != null) {							
					listItems[i] = new ListItem(relatedProp.getText(),relatedProp.getText());							
				}
			}
			return listItems;
		}

		return null;
		
	}
	
	public PersistableEntityModel selectByProperty(PersistableEntityModel entity, PropertyModel filterProp) throws Exception{
		FilterModel filter = new FilterModel();			
		filter.setFieldValue(filterProp);
		
		FetchedEntities result = fetch(entity, filter);
		if(result == null || result.entities == null || result.entities.length == 0){
			return null;
		}
		
		return result.entities[0];
	}
	

}
